﻿using UnityEngine;
using System.Collections;
using LuaInterface;
using LuaFramework;
using System;
using System.IO;
using UnityEngine.Networking;

namespace Demo
{
    public class LuaManager : SingletonBehaviour<LuaManager>
    {
        public LuaState lua { get; private set; }
        private LuaLoader loader;
        private LuaLooper loop = null;

        // Use this for initialization
        void Awake()
        {
            loader = new LuaLoader();
            lua = new LuaState();
            this.OpenLibs();
            lua.LuaSetTop(0);

            LuaBinder.Bind(lua);
            DelegateFactory.Init();
            LuaCoroutine.Register(lua, this);
        }

        public void InitStart()
        {
            InitLuaPath();
            InitLuaBundle(() =>
            {
                this.lua.Start(); //启动LUAVM
                this.StartMain();
                this.StartLooper();
            });
        }

        void StartLooper()
        {
            loop = gameObject.AddComponent<LuaLooper>();
            loop.luaState = lua;
        }

        //cjson 比较特殊，只new了一个table，没有注册库，这里注册一下
        protected void OpenCJson()
        {
            lua.LuaGetField(LuaIndexes.LUA_REGISTRYINDEX, "_LOADED");
            lua.OpenLibs(LuaDLL.luaopen_cjson);
            lua.LuaSetField(-2, "cjson");

            lua.OpenLibs(LuaDLL.luaopen_cjson_safe);
            lua.LuaSetField(-2, "cjson.safe");
        }

        void StartMain()
        {
            lua.DoFile("MyDemo/DemoMain.lua");

            LuaFunction main = lua.GetFunction("Main");
            main.Call();
            main.Dispose();
            main = null;
        }

        /// <summary>
        /// 初始化加载第三方库
        /// </summary>
        void OpenLibs()
        {
            lua.OpenLibs(LuaDLL.luaopen_pb);
            lua.OpenLibs(LuaDLL.luaopen_sproto_core);
            lua.OpenLibs(LuaDLL.luaopen_protobuf_c);
            lua.OpenLibs(LuaDLL.luaopen_lpeg);
            lua.OpenLibs(LuaDLL.luaopen_bit);
            lua.OpenLibs(LuaDLL.luaopen_socket_core);
            this.OpenLuaSocket();
            this.OpenCJson();
        }

        #region luaide 调试库添加

        //如果项目中没有luasocket 请打开
        [MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
        static int LuaOpen_Socket_Core(IntPtr L)
        {
            return LuaDLL.luaopen_socket_core(L);
        }

        protected void OpenLuaSocket()
        {
            LuaConst.openLuaSocket = true;
            lua.BeginPreLoad();
            lua.RegFunction("socket.core", LuaOpen_Socket_Core);
            lua.EndPreLoad();
        }

        #endregion

        /// <summary>
        /// 初始化Lua代码加载路径
        /// </summary>
        void InitLuaPath()
        {
#if UNITY_WEBGL
            lua.AddSearchPath(Util.DataPath + "lua");
#else
            if (AppConst.ApplicationDataPath)
            {
                string rootPath = AppConst.FrameworkRoot;
                lua.AddSearchPath(rootPath + "/Lua");
                lua.AddSearchPath(rootPath + "/ToLua/Lua");
            }
            else
            {
                lua.AddSearchPath(Util.DataPath + "lua");
            }
#endif
        }

        /// <summary>
        /// 初始化LuaBundle
        /// </summary>
        void InitLuaBundle(Action back)
        {
            if (loader.beZip)
            {
#if UNITY_WEBGL
                StartCoroutine(_AddBundle("lua/lua.unity3d", back));
#else
                loader.AddBundle("lua/lua.unity3d");
                back.Invoke();
#endif
            }
        }

        public IEnumerator _AddBundle(string bundleName, Action back)
        {
            yield return 0;
            var uri = new System.Uri(Path.Combine(Application.streamingAssetsPath, bundleName));
            UnityWebRequest request = UnityWebRequest.Get(uri);
            yield return request.SendWebRequest();
            if (request.isNetworkError || request.isHttpError)
            {
                Debug.Log(request.error);
            }
            else
            {
                AssetBundle ab = AssetBundle.LoadFromMemory(request.downloadHandler.data);
                loader.AddBundle(bundleName, ab);
            }

            back.Invoke();
        }

        public void DoFile(string filename)
        {
            lua.DoFile(filename);
        }

        // Update is called once per frame
        public object[] CallFunction(string funcName, params object[] args)
        {
            LuaFunction func = lua.GetFunction(funcName);
            if (func != null)
            {
                return func.LazyCall(args);
            }

            return null;
        }

        public void LuaGC()
        {
            lua.LuaGC(LuaGCOptions.LUA_GCCOLLECT);
        }

        public void Close()
        {
            if (loop != null)
            {
                loop.Destroy();
                loop = null;
            }

            if (lua != null)
            {
                lua.Dispose();
                lua = null;
            }

            if (loader != null)
            {
                loader.Dispose();
                loader = null;
            }
        }

        public object require(string fileName)
        {
            int top = lua.LuaGetTop();
            string error = null;
            object result = null;

            if (lua.LuaRequire(fileName) != 0)
            {
                error = lua.LuaToString(-1);
            }
            else
            {
                if (lua.LuaGetTop() > top)
                {
                    result = lua.ToVariant(-1);
                }
            }

            lua.LuaSetTop(top);

            if (error != null)
            {
                throw new LuaException(error);
            }

            return result;
        }

        protected override void OnDestroy()
        {
            Close();
            base.OnDestroy();
        }
    }
}